tags:
- SQL
accession_number
which is a unique ID used by the museum internally. There is, too, a date indicating when the art was acquired.mfa.db
. Next, we read the schema file schema.sql
into the database. This schema file, already given to us, helps us create the table collections
.SELECT * FROM "collections";
This should give us an empty result, because the table doesn’t have any data yet.INSERT INTO
is used to insert a row of data into a given table.
INSERT INTO "collections" ("id", "title", "accession_number", "acquired")
VALUES (1, 'Profusion of flowers', '56.257', '1956-04-12');
We can see that this command requires the list of columns in the table that will receive new data and the values to be added to each column, in the same order.INSERT INTO
command returns nothing, but we can run a query to confirm that the row is now present in collections
.
SELECT * FROM "collections";
INSERT INTO "collections" ("title", "accession_number", "acquired")
VALUES ('Farmers working at dawn', '11.6152', '1911-08-03');
We can check that this row has been inserted with an id
of 2 by running
SELECT * FROM "collections";
Notice that the way SQLite fills out the primary key values is by incrementing the previous primary key—in this case, 1.If we delete a row with the primary key 1, will SQLite automatically assign a primary key of 1 to the next inserted row?
schema.sql
will pull up the schema for the database.
CREATE TABLE "collections" (
"id" INTEGER,
"title" TEXT NOT NULL,
"accession_number" TEXT NOT NULL UNIQUE,
"acquired" NUMERIC,
PRIMARY KEY("id")
);
Runtime error: UNIQUE constraint failed: collections.accession_number (19)
.UNIQUE
constraint in this scenario.NULL
title, violating the NOT NULL
constraint.
INSERT INTO "collections" ("title", "accession_number", "acquired")
VALUES(NULL, NULL, '1900-01-10');
On running this, we will again see an error that looks like Runtime error: NOT NULL constraint failed: collections.title (19)
.INSERT INTO
command.
collections
table.
INSERT INTO "collections" ("title", "accession_number", "acquired")
VALUES
('Imaginative landscape', '56.496', NULL),
('Peonies and butterfly', '06.1899', '1906-01-01');
The museum may not always know exactly when a painting was acquired, hence it is possible for the acquired
value to be NULL
, as is the case for the first painting we just inserted.SELECT * FROM "collections";
mfa.db
and then remove it.mfa.csv
that contains the data we need. On opening up this file, we can note that the first row contains the column names, which match exactly with the column names of our table collections
as per the schema.mfa.db
and read the schema file as we did earlier..import --csv --skip 1 mfa.csv collections
The first argument, --csv
indicates to SQLite that we are importing a CSV file. This will help SQLite parse the file correctly. The second argument indicates that the first row of the CSV file (the header row) needs to be skipped, or not inserted into the table.collections
table to see that every painting from mfa.csv
has been successfully imported into the table.mfa.csv
in our codespace and delete the id
column from the header row, along with the values in each column. This is what mfa.csv
should look like once we finish editing:
title,accession_number,acquired
Profusion of flowers,56.257,1956-04-12
Farmers working at dawn,11.6152,1911-08-03
Spring outing,14.76,1914-01-08
Imaginative landscape,56.496,
Peonies and butterfly,06.1899,1906-01-01
collections
table.
DELETE FROM "collections";
collections
table (as per our schema) must have four columns in every row. This new CSV file contains only three columns for every row. Hence, we cannot proceed to import in the same way we did before..import --csv mfa.csv temp
Notice how we don’t use the argument --skip 1
with this command. This is because SQLite is capable of recognizing the very first row of CSV data as the header row, and converts those into the column names of the new temp
table.temp
table by querying it.
SELECT * FROM "temp";
temp
and move it to collections
, which was the goal all along! We can use the following command to achieve this.
INSERT INTO "collections" ("title", "accession_number", "acquired")
SELECT "title", "accession_number", "acquired" FROM "temp";
In this process, SQLite will automatically add the primary key values in the id
column.temp
table once we’re done moving data.
DROP TABLE "temp";
Can we place columns in specific positions while inserting into a table?
INSERT INTO
command, we usually can’t change the ordering of the column names themselves. The order of column names follows the same order used while creating the table.What happens if one of the multiple rows we are trying to insert violates a table constraint?
After inserting data from the CSV, one of the cells was empty and not
NULL
. Why did this happen?
acquired
values was missing! This was interpreted as text and hence, read into the table as an empty text value. We can run queries on the table after importing to convert these empty values into NULL
if required.collections
. (We don’t want to actually run this command now or we’ll lose all the data in the table!)
DELETE FROM "collections";
collections
we can run:
DELETE FROM "collections"
WHERE "title" = 'Spring outing';
NULL
we can run
DELETE FROM "collections"
WHERE "acquired" IS NULL;
SELECT * FROM "collections";
We see that the “Spring outing” and “Imaginative landscape” paintings are not in the table anymore.DELETE FROM "collections"
WHERE "acquired" < '1909-01-01';
Using the <
operator here, we are finding the paintings acquired before January 1, 1909. These are the paintings that will be deleted on running the query.artists
and collections
tables have primary keys—the ID columns. The created
table references these IDs in its two foreign key columns.created
with an artist_id
of 3? Let’s try it out.mfa.db
, we can now see the updated schema by running the .schema
command. The created
table does indeed have two foreign key constraints, one for the artist ID and one for the collection ID.artists
table.
DELETE FROM "artists"
WHERE "name" = 'Unidentified artist';
On running this, we get an error very similar to ones we have seen before in this class: Runtime error: FOREIGN KEY constraint failed (19)
. This error notifies us that deleting this data would violate the foreign key constraint set up in the created
table.created
table before deleting from the artists
table.
DELETE FROM "created"
WHERE "artist_id" = (
SELECT "id"
FROM "artists"
WHERE "name" = 'Unidentified artist'
);
This query effectively deletes the artist’s affiliation with their work. Once the affiliation no longer exists, we can delete the artist’s data without violating the foreign key constraint. To do this, we can run
DELETE FROM "artists"
WHERE "name" = 'Unidentified artist';
ON DELETE
followed by the action to be taken.
ON DELETE RESTRICT
: This restricts us from deleting IDs when the foreign key constraint is violated.ON DELETE NO ACTION
: This allows the deletion of IDs that are referenced by a foreign key and nothing happens.ON DELETE SET NULL
: This allows the deletion of IDs that are referenced by a foreign key and sets the foreign key references to NULL
.ON DELETE SET DEFAULT
: This does the same as the previous, but allows us to set a default value instead of NULL
.ON DELETE CASCADE
: This allows the deletion of IDs that are referenced by a foreign key and also proceeds to cascadingly delete the referencing foreign key rows. For example, if we used this to delete an artist ID, all the artist’s affiliations with the artwork would also be deleted from the created
table.FOREIGN KEY("artist_id") REFERENCES "artists"("id") ON DELETE CASCADE
FOREIGN KEY("collection_id") REFERENCES "collections"("id") ON DELETE CASCADE
Now running the following DELETE
statement will not result in an error, and will cascade the deletion from the artists
table to the created
table:
DELETE FROM "artists"
WHERE "name" = 'Unidentified artist';
To check that this cascading deletion worked, we can query the created
table:
SELECT * FROM "created";
We observe that none of the rows have an ID of 3 (the ID of the artist deleted from the artists
table).We just deleted an artist with the ID of 3. Is there any way to make the next inserted row have an ID of 3?
AUTOINCREMENT
keyword while creating a column to indicate that any deleted ID should be repurposed for a new row being inserted into the table.created
table using the above syntax.
UPDATE "created"
SET "artist_id" = (
SELECT "id"
FROM "artists"
WHERE "name" = 'Li Yin'
)
WHERE "collection_id" = (
SELECT "id"
FROM "collections"
WHERE "title" = 'Farmers working at dawn'
);
The first part of this query specifies the table to be updated. The next part retrieves the ID of Li Yin to set as the new ID. The last part selects the row(s) in created
which will be updated with the ID of Li Yin, which is the painting “Farmers working at dawn”!